const path = require('path')
const fs = require('fs')
const vm = require('vm')

class Module{
    constructor(id){
        this.id = id

    }
}

// function Module(id) {
//     this.id = id
//     this.exports = {}
// }
// Module.wrapper = [
//     `(function(exports,require,module,__filename,__dirname){`,
//     `})`
// ];

// Module._extensions = {
//     '.js'(module) {
//         let content = fs.readFileSync(module.id, 'utf8')
//         // 包裹一层函数
//         content = Module.wrapper[0] + content +Module.wrapper[1]
//         // 通过vm执行content字符串生成一个函数
//         let fn = vm.runInThisContext(content)
//         let exports = module.exports
//         let dirname = path.dirname(module.id);
//         // 执行此函数 用户会自动的给module.exports 赋值
//         // 此函数执行的时候 this 发生了变化
//         fn.call(exports,exports,req,module,module.id,dirname)

//     },
//     '.json'(module) {
//         let content = fs.readFileSync(module.id, 'utf8')
//         module.exports = JSON.parse(content)
//     }
// }
// Module._resolveFilename = function (id) {
//     let filePath = path.resolve(__dirname, id)
//     // 判断是否是绝对路径
//     if (fs.existsSync(filePath)) return filePath

//     let exts = Object.keys(Module._extensions)
//     for (let index = 0; index < exts.length; index++) {
//         let p = filePath + exts[i]
//         if (fs.existsSync(p)) return p
//     }
//     // 不存在尝试添加后缀，则抛错
//     throw new Error('模块不存在')
// }

// Module.prototype.load = function () {
//     // 获取扩展名
//     let extName = path.extname(this.id)
//     // 
//     Module._extensions[extName](this)
// }
// // 全局的缓存区，用来缓存模块
// // 如果同一路径下导入多次，则会调用缓存中的值，(性能优化点)
// Module._cache = {}
// function req(id) {
//     // 将路径变成绝对路径，可能id后面没有后缀，需要依次添加后缀.js,.json 
//     let filePath = Module._resolveFilename(id)
//     // 如果缓存中有 直接使用上一次缓存的结果
//     if(Module._cache[filePath]){
//         return Module._cache[filePath].exports
//     }
//     // 生产module实例，将文件绝对路径作为module属性存入
//     let module = new Module(filePath)
//     // 将模块进行缓存
//     Module._cache[filePath] = module  
//     module.load()
//     return module.exports
// }

var r = req('./a.js'),
r = req('./a.js')
r = req('./a.js')
console.log(r);
setInterval(() => {
    console.log(r);
}, 1000);

// 基本类型和引用类型的区别：（涉及到按值传递）
// 1.如果导出的是一个具体的值，这个值变化了，也不会被重新渲染
// 2.如果导出的是一个引用类型，如果改变了引用类型的值，那么重新获取的就是这个更新后的内容

// commonjs导出的是值或者引用地址
// es6导出的变量


// commonjs步骤
// 1.require是一个函数 接受一个./a
// 2.Module._load 拿到文件路径进行加载 ./a
// 3.内部会通过 _resolveFilename 方法 将a 文件转化成绝对路径
// 4.通过绝对路径来进行实现
// 5.内部会有判断是否是绝对路径 来推测是不是一个内置模块
// 6.创建一个模块 （id, exports）
// 7.会将模块缓存起来
// 8.真正的加载模块
// 9.通过用户的路径 获取了一个绝对路径 根据这个路径创造了一个模块  {id:文件路径， exports 导出的结果}
// 10.module.load  内部会根据后缀名 解析文件 调用对应的处理方法
// 11.最终返回的是exports 对象


// 分析实现步骤

// 1.导入相关模块，创建一个Require方法。
// 2.抽离通过Module._load方法，用于加载模块。
// 3.Module.resolveFilename 根据相对路径，转换成绝对路径。
// 4.缓存模块 Module._cache，同一个模块不要重复加载，提升性能。
// 5.创建模块 id: 保存的内容是 exports = {}相当于this。
// 6.利用tryModuleLoad(module, filename) 尝试加载模块。
// 7.Module._extensions使用读取文件。
// 8.Module.wrap: 把读取到的js包裹一个函数。
// 9.将拿到的字符串使用runInThisContext运行字符串。
// 10.让字符串执行并将this改编成exports